package com.idlecode.util.sm;

import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;

import java.math.BigInteger;
import java.security.SecureRandom;
import java.security.Security;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

public class SM2Util {
	static {
		// 注册 BouncyCastle 提供者
		Security.addProvider(new BouncyCastleProvider());
	}

	private final ECDomainParameters domainParams;

    /**
     *
     * @param curveName 曲线名称
     */
	public SM2Util(String curveName){
		X9ECParameters parameters = GMNamedCurves.getByName(curveName);
		this.domainParams = new ECDomainParameters(parameters.getCurve(), parameters.getG(), parameters.getN());
	}

    /**
     * 获取非对称密码密钥对
     */
	private AsymmetricCipherKeyPair generateKeyPair() {
		ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
		SecureRandom random = new SecureRandom();
		ECKeyGenerationParameters keyGenParams = new ECKeyGenerationParameters(domainParams, random);
		keyPairGenerator.init(keyGenParams);
		return keyPairGenerator.generateKeyPair();
	}

	public MyKeyPair genKeyPair(){
		AsymmetricCipherKeyPair keyPair = this.generateKeyPair();
		ECPrivateKeyParameters privateKeyParameters = (ECPrivateKeyParameters) keyPair.getPrivate();
		ECPublicKeyParameters publicKeyParameters = (ECPublicKeyParameters) keyPair.getPublic();

		// 显示公钥和私钥
		String privateKey = Hex.toHexString(privateKeyParameters.getD().toByteArray());
		String publicKey = Hex.toHexString(publicKeyParameters.getQ().getEncoded(false));
		MyKeyPair myKeyPair = new MyKeyPair();
		myKeyPair.setPublicKey(publicKey);
		myKeyPair.setPrivateKey(privateKey);
		return myKeyPair;
	}

    /**
     * 获取支持的曲线名称
     */
    public static List<String> getCurveNames () {
        List<String> curveNameList = new ArrayList<>();
        Enumeration<String> curveNames = GMNamedCurves.getNames();
        while (curveNames.hasMoreElements()) {
            String curveName = curveNames.nextElement();
            curveNameList.add(curveName);
            /*X9ECParameters params = GMNamedCurves.getByName(curveNamettt);
            printCurveParameters(params);*/
        }
        return curveNameList;
    }

    /**
     * 打印曲线名称的参数
     */
	private static void printCurveParameters(X9ECParameters params) {
		ECCurve curve = params.getCurve();
		ECPoint g = params.getG();

		System.out.println("p (Field Characteristic): " + curve.getField().getCharacteristic().toString(16));
		System.out.println("a: " + curve.getA().toBigInteger().toString(16));
		System.out.println("b: " + curve.getB().toBigInteger().toString(16));
		System.out.println("n (Order): " + params.getN().toString(16));
		System.out.println("Gx: " + g.getAffineXCoord().toBigInteger().toString(16));
		System.out.println("Gy: " + g.getAffineYCoord().toBigInteger().toString(16));
	}

	// 加密方法
	public String encrypt(String inputData, String publicKeyHex) throws Exception {
		// 将十六进制公钥转换为字节数组
		byte[] publicKeyBytes = Hex.decode(publicKeyHex);

		// 通过字节数组还原公钥
		X9ECParameters parameters = GMNamedCurves.getByName("sm2p256v1");
		ECCurve curve = parameters.getCurve();
		ECPoint publicKeyPoint = curve.decodePoint(publicKeyBytes);
		ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(publicKeyPoint, domainParams);

		// 初始化 SM2 引擎进行加密
		SM2Engine engine = new SM2Engine();
		ParametersWithRandom parametersWithRandom = new ParametersWithRandom(publicKeyParameters, new SecureRandom());
		engine.init(true, parametersWithRandom); // true 表示加密模式

		// 将输入数据转换为字节
		byte[] inputDataBytes = inputData.getBytes();
		byte[] encryptedBytes = engine.processBlock(inputDataBytes, 0, inputDataBytes.length);

		// 将加密后的字节转换为十六进制字符串
		return Hex.toHexString(encryptedBytes);
	}

	// 解密方法
	public String decrypt(String encryptedDataHex, String privateKeyHex) throws Exception {
		// 将十六进制私钥转换为字节数组
		byte[] privateKeyBytes = Hex.decode(privateKeyHex);

		// 通过字节数组还原私钥
		BigInteger privateKeyD = new BigInteger(1, privateKeyBytes); // 1 表示正数
		ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParams);

		// 初始化 SM2 引擎进行解密
		SM2Engine engine = new SM2Engine();
		engine.init(false, privateKeyParameters); // false 表示解密模式

		// 将加密的十六进制字符串转换为字节
		byte[] encryptedDataBytes = Hex.decode(encryptedDataHex);
		byte[] decryptedBytes = engine.processBlock(encryptedDataBytes, 0, encryptedDataBytes.length);

		// 将解密后的字节转换为字符串
		return new String(decryptedBytes);
	}


    public static void main(String[] args) {
        try {
            SM2Util sm2Util = new SM2Util("sm2p256v1");
            // 生成公私钥对
            AsymmetricCipherKeyPair keyPair = sm2Util.generateKeyPair();
            ECPrivateKeyParameters privateKeyParameters = (ECPrivateKeyParameters) keyPair.getPrivate();
            ECPublicKeyParameters publicKeyParameters = (ECPublicKeyParameters) keyPair.getPublic();

            // 显示公钥和私钥
            String privateKey = Hex.toHexString(privateKeyParameters.getD().toByteArray());
            String publicKey = Hex.toHexString(publicKeyParameters.getQ().getEncoded(false));
            System.out.println("Private Key: " + privateKey);
            System.out.println("Public Key: " + publicKey);

            String inputData = "Hello World";

            // 使用 String 键值进行加密和解密
            String encryptedData = sm2Util.encrypt(inputData, publicKey);
            System.out.println("加密 Data: " + encryptedData);

            String text = "";

            String decryptedData = sm2Util.decrypt(text, "");
            System.out.println("解密 Data: " + decryptedData);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}